package core;

import object.Player;
import object.BulletEnemy;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;

import java.awt.Font;
import java.awt.FontMetrics;
import utilities.ImageLoader;
import utilities.ImagePlayer;
import utilities.ImagePlayerWatcher;
import utilities.ScrollManager;
import utilities.TileManager;

/**
 *
 * Class in which the game takes place
 */


public class PlayState extends JPanel implements Runnable, ImagePlayerWatcher
{
    private static final int PWIDTH=500;
    private static final int PHEIGHT=360;
    private static final int NO_DELAYS_PER_YIELD=16;
    private static final int MAX_FRAME_SKIPS=5;
    private static final int MAX_HITS=5;
    private static final String IMS_INFO="imInfo.txt";
    private static final String TILE_INFO="tileInfo.txt";

    private long period;
    private long gameStartTime;

    private int numHits=0;
    private int timeSpentInGame;

    private volatile boolean paused=true;
    private volatile boolean running=false;
    private volatile boolean gameOver=false;
    private volatile boolean gameTitle=true;

    //private Gambolputty gameTop;
    private Thread gameplayer;
    private TileManager tileMan;
    private ScrollManager scrollMan;
    private Player player;
    private BulletEnemy enemy;

    private Graphics dbg;
    private Image dbImage=null;
    private BufferedImage titleScreen;

    private ImagePlayer hitPlayer=null;
    private ImagePlayer transformPlayer2=null;
    private ImagePlayer transformPlayer3=null;

    private boolean showHit=false;
    private boolean transform2=false;
    private boolean transform3=false;

    private int explWidth, explHeight;
    private int trans2Width, trans2Height;
    private int trans3Width, trans3Height;
    private int xHit,yHit;
    private int xTrans,yTrans;
    private Font hudFont;
    private FontMetrics metrics;


    //private InputManager keyManager;

    //private Action moveLeft;
    //private Action moveRight;
    //private Action jump;
    //private Action duck;
    //private Action exit;

    public PlayState(long period)
    {
        this.period=period;

        //initInput();

        setDoubleBuffered(false);
        setBackground(Color.white);
        setPreferredSize(new Dimension(PWIDTH, PHEIGHT));

        setFocusable(true);
        requestFocus();

        addKeyListener(new KeyAdapter()
        {
            public void keyPressed(KeyEvent e)
            {
                processKey(e);
            }
        });

        ImageLoader imLoader=new ImageLoader(IMS_INFO);
        titleScreen=imLoader.getImage("menuBackground");

        tileMan=new TileManager(PWIDTH,PHEIGHT,TILE_INFO,imLoader);
        int tileMoveSize=tileMan.getMoveSize();

        scrollMan=new ScrollManager(PWIDTH,PHEIGHT,tileMoveSize,imLoader);

        player=new Player(PWIDTH,PHEIGHT,tileMoveSize,tileMan,imLoader,(int)(period/1000000L),this);
        enemy=new BulletEnemy(PWIDTH,PHEIGHT,imLoader,player,this);

        hitPlayer=new ImagePlayer("explosion",(int)(period/1000000L),0.5,false,imLoader);
        BufferedImage explosionIm=imLoader.getImage("explosion");

        transformPlayer2=new ImagePlayer("transform2",(int)(period/2000000L),0.5,false,imLoader);
        BufferedImage transform2Im=imLoader.getImage("transform2");
        
        transformPlayer3=new ImagePlayer("transform3",(int)(period/3000000L),0.5,false,imLoader);
        BufferedImage transform3Im=imLoader.getImage("transform3");

        explWidth=explosionIm.getWidth();
        explHeight=explosionIm.getHeight();
        trans2Width=transform2Im.getWidth();
        trans2Height=transform2Im.getHeight();
        trans3Width=transform3Im.getWidth();
        trans3Height=transform3Im.getHeight();

        hitPlayer.setWatcher(this);
        transformPlayer2.setWatcher(this);
        transformPlayer3.setWatcher(this);

        hudFont=new Font("Arial",Font.PLAIN,12);
        metrics=this.getFontMetrics(hudFont);
    }

//    private void initInput()
//    {
//        moveLeft=new Action("moveLeft");
//        moveRight=new Action("moveRight");
//        jump = new Action("jump",Action.DETECT_INITAL_PRESS_ONLY);
//        exit = new Action("exit",Action.DETECT_INITAL_PRESS_ONLY);
//
//        keyManager = new InputManager(this);
//
//        keyManager.mapToKey(moveLeft,KeyEvent.VK_LEFT);
//        keyManager.mapToKey(moveRight,KeyEvent.VK_RIGHT);
//        keyManager.mapToKey(jump,KeyEvent.VK_SPACE);
//        keyManager.mapToKey(exit,KeyEvent.VK_ESCAPE);
//    }
//
//    private void checkInput()
//    {
//      if (moveLeft.isPressed())
//      {
//      }
//      if (moveRight.isPressed())
//      {
//      }
//    }

    private void processKey(KeyEvent e)
    {
        int keyCode = e.getKeyCode();

        if (keyCode==KeyEvent.VK_ESCAPE)
            running = false;
        if (keyCode==KeyEvent.VK_1)
            player.setPlayerForm(1);
        if (keyCode==KeyEvent.VK_2)
        {
            player.setPlayerForm(2);
            player.transform2();
        }
        if ((keyCode==KeyEvent.VK_3)&&(player.getPlayerForm()==2))
        {
            player.setPlayerForm(3);
            player.transform3();
        }
        if ((keyCode==KeyEvent.VK_B)&&(player.getPlayerForm()==2))
            player.breakdance();
        if ((keyCode==KeyEvent.VK_D)&&(player.getPlayerForm()==2))
            player.dance();

        if (keyCode==KeyEvent.VK_ENTER)
        {
            if (gameTitle)
                gameTitle=false;
            paused=false;
        }
        if (keyCode==KeyEvent.VK_P)
        {
            if (!paused)
                paused=true;
            else
                paused=false;
        }

        if (!paused&&!gameOver)
        {
            if (keyCode==KeyEvent.VK_LEFT)
            {
                player.moveLeft();
                scrollMan.moveRight();
                tileMan.moveRight();
                
            }
            else
                if (keyCode==KeyEvent.VK_RIGHT)
                {
                    player.moveRight();
                    scrollMan.moveLeft();
                    tileMan.moveLeft();
                }
                else
                    if (keyCode==KeyEvent.VK_UP)
                    {
                        player.jump();
                    }
                    else
                        if (keyCode == KeyEvent.VK_DOWN)
                            {
                                player.stayStill();
                                scrollMan.stayStill();
                                tileMan.stayStill();
                            }

        }
    }

    public void run()
    {
        long beforeTime, afterTime, timeDiff, sleepTime;
        long overSleepTime = 0L;
        int noDelays = 0;
        long excess = 0L;

        gameStartTime=System.nanoTime();
        beforeTime=gameStartTime;

	running = true;

	while(running)
        {
            gameUpdate();
            gameRender();
            paintScreen();

            afterTime=System.nanoTime();
            timeDiff=afterTime-beforeTime;
            sleepTime=(period-timeDiff)-overSleepTime;

            if (sleepTime>0)
            {
                try
                {
                    Thread.sleep(sleepTime/1000000L);
                }
                catch(InterruptedException ex){}

                overSleepTime=(System.nanoTime()-afterTime)-sleepTime;
            }
            else
            {
                excess-=sleepTime;
                overSleepTime=0L;

                if (++noDelays >=NO_DELAYS_PER_YIELD)
                {
                    Thread.yield();
                    noDelays = 0;
                }
            }

            beforeTime=System.nanoTime();

            int skips=0;
            while((excess>period)&&(skips<MAX_FRAME_SKIPS))
            {
                excess-=period;
                gameUpdate();
                skips++;
            }
        }
        System.exit(0);
    }

    public void gameUpdate()
    {
        ////checkInput();
        if (!paused&&!gameOver)
        {
            if (player.collisionDetection())
            {
                player.stayStill();
                tileMan.stayStill();
                scrollMan.stayStill();
            }
            scrollMan.update();
            tileMan.update();
            player.updateSprite();
            enemy.updateSprite();

            if (showHit)
                hitPlayer.updateTick();
            if (transform2)
                transformPlayer2.updateTick();
            if (transform3)
                transformPlayer3.updateTick();
        }
    }

    private void gameRender()
    {
        if (dbImage==null)
        {
            dbImage=createImage(PWIDTH,PHEIGHT);
            if (dbImage==null)
            {
                System.out.println("dbImage is null");
                return;
            }
             else
                dbg = dbImage.getGraphics();
        }

        dbg.setColor(Color.white);
        dbg.fillRect(0,0,PWIDTH,PHEIGHT);

        if (gameTitle)
        {
            dbg.drawImage(titleScreen,PWIDTH,PHEIGHT,null);
        }

        scrollMan.display(dbg);
        tileMan.display(dbg);
        player.drawSprite(dbg);
        enemy.drawSprite(dbg);

        hud(dbg);
        
        if (showHit)
            dbg.drawImage(hitPlayer.getCurrentImage(),xHit,yHit,null);
        if (transform2)
            dbg.drawImage(transformPlayer2.getCurrentImage(),xTrans,yTrans,null);
        if (transform3)
            dbg.drawImage(transformPlayer3.getCurrentImage(),xTrans,yTrans,null);

        if (gameOver)
            gameOver(dbg);

    }

    public void sequenceEnded(String imageName)
    {
        showHit=false;
        hitPlayer.restart(0);
        if (transform2)
            player.setImage("runningRight2");
        if (transform3)
            player.setImage("runningRight3");
        transform2=false;
        transform3=false;
        transformPlayer2.restart(0);
        transformPlayer3.restart(0);
        
        if (numHits>=MAX_HITS)
        {
            gameOver=true;
        }
  }

    public void addNotify()
    {
        super.addNotify();
        startGame();
    }

    public void startGame()
    {
       if (gameplayer==null||!running)
        {
            gameplayer=new Thread(this);
            gameplayer.start();
        }
    }

    public void stopGame()
    {
        running=false;
    }

    private void gameOver(Graphics g)
    {
        scrollMan.setGameOver();
    }

    public void enemyHitPlayer(int x, int y)
    {
        if (!showHit)
        {
                showHit=true;
                xHit=x-explWidth/2;
                yHit=y-explHeight/2;
                numHits++;
        }
    }

    public void transform2(int x,int y)
    {
        if (!transform2)
        {
                xTrans=x-trans2Width/2;
                yTrans=y-trans2Height/2;
                transform2=true;
        }
    }

    public void transform3(int x,int y)
    {
        if (!transform3)
        {
                xTrans=x-trans3Width/2;
                yTrans=y-trans3Height/2;
                transform3=true;
        }
    }

    private void hud(Graphics g)
    {
        if ((!paused)&&(!gameOver))
            timeSpentInGame=(int)((System.nanoTime()-gameStartTime)/1000000000L);
	g.setColor(Color.blue);
        g.setFont(hudFont);
	g.drawString("Life left: "+numHits+"/"+MAX_HITS,15,25);
	g.drawString("Time: "+timeSpentInGame+" secs",15,50);
	g.setColor(Color.black);
    }

    public void playerHitEnemy()
    {
        
    }

    private void paintScreen()
    {
        Graphics g;
        try
        {
            g=this.getGraphics();
            if((g!=null)&&(dbImage!=null))
                g.drawImage(dbImage,0,0,null);
            Toolkit.getDefaultToolkit().sync();
            g.dispose();
        }
        catch (Exception e)
        {
            System.out.println("Graphics context error: " + e);
        }
    }

}
